
package com.esdk.sql.orm;

import cn.hutool.json.JSONUtil;
import cn.hutool.json.XML;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.esdk.esdk;
import com.esdk.sql.*;
import com.esdk.utils.*;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Stream;

abstract public class ParentRow<T extends ParentRow> implements IRow,Cloneable,IConnectionable,ITable,IChangeable,Comparable<T>{
	private static final short SAVE=0,INSERT=1,UPDATE=2,DELETE=3;
	public static String VersionFieldName="version";
	private transient boolean isInited;
	protected boolean isChanged;
	protected HashMap<String,Object> record=new HashMap<>();
	private transient HashMap<String,Object> changedMap,originalMap,newVaulesMap;
	protected transient Connection conn;
	protected transient ISQL sqlOperator;
	protected boolean isExistRecord; //是否数据库已存在该记录
	private transient short operatorFlag=SAVE;
	protected transient boolean isAutoIncrement;
	protected transient boolean isCheckDirty,isCheckVersion;
	protected String[] columns;
	private boolean isForceUpdate=false;
	private transient int useCacheSec=Constant.ResetUseCacheSec;
	private boolean allowSetNullValue=true; //默认允许设置字段值为null，为false时，null值字段无法update
	private boolean useSessionCache=false;
	private boolean isPrintSql=true;
	private String primaryKeyName;
	private String tableName;

	/*
	 * private short uniqueOperatorFlag=-1; private short insertFlag=1,updateFlag=2,deleteFlag=3;
	 */
	public void setConnection(Connection con){
		conn=con;
	}

	public void initMaps() {
		if(!isInited) {
			if(changedMap==null)
			 changedMap=new HashMap();
			if(originalMap==null)
			 originalMap=new HashMap(4);
			if(newVaulesMap==null)
			 newVaulesMap=new HashMap();
			isInited=true;
		}
	}

	abstract public T newSelf();

	public T setPrimaryKey(Object value){
		set(getPrimaryKeyName(),value);
		return (T)this;
	}

	@JSONField(serialize=false)
	public Object getPrimaryKey(){
		return get(getPrimaryKeyName());
	}

	public T setPKID(Long value){
		set(getPrimaryKeyName(),value);
		return (T)this;
	}

	public Long getPKID(){
		return esdk.obj.convert(get(getPrimaryKeyName()),Long.class);
	}

	@JSONField(serialize=false)
	abstract public Object getMetaData();

	public Map record(){
		return record;
	}

	protected void setNewValue(String key,Object newvalue){
		if(allowSetNullValue||newvalue!=null){
			initMaps();
			newVaulesMap.put(key,newvalue);
			if(!isSameValue(key,newvalue)){
				isChanged=true;
				Object oldvalue=record.put(key,newvalue);
				addOriginalMap(key,oldvalue);
				changedMap.put(key,newvalue);
			}
		}
	}

	private void addOriginalMap(String key,Object oldValue){
		if(!originalMap.containsKey(key))
			originalMap.put(key,oldValue);
	}

	public Object get(String key){
		return esdk.str.get(record,key);
	}

	public <R> R get(String key,R def){
		return (R)record.getOrDefault(key,def);
	}


	/**不会根据字段类型自动做格式的转换，因此处理效率比较高*/
	public void set(int pos,String value){
		set(getColumns()[pos],value);
	}

	public void set(String key,Object v){
		setNewValue(key,v);
	}

	public void deleteColumn(String ...removeColumns){
		for (String col : removeColumns) {
			record.remove(col);
			originalMap.remove(col);
			newVaulesMap.remove(col);
			changedMap.remove(col);
			if(changedMap.isEmpty())
				isChanged = false;
		}
		this.columns=esdk.array.remove(this.columns,removeColumns);
	}

	/**会根据表的字段类型自动转换，如果是自定义字段则不做类型转换*/
	@Override public T put(String key,Object newValue){
		try{
			Object obj=valueOf(key,newValue);
			setNewValue(key,obj);
		}catch(Exception e){
			throw new RuntimeException(e);
		}
		return (T)this;
	}

	/**支持基础数据类型、JSON、ResultSet对象*/
	private Object valueOf(String columnName,Object value) throws Exception{
		if(value instanceof JSON)
			return value;
		if(value instanceof Between)
			return null;
		if(value instanceof IResultSet resultSet)
			return resultSet.toJsonArray(true,true);
		Class columnClass=getColumnClass(columnName);
		if(columnClass!=null&&columnClass.equals(Boolean.class))
			return esdk.obj.toBoolean(value);
		else if(columnClass!=null&&!columnClass.equals(String.class)&&(value!=null&&value.toString().length()==0))
			return value;
		else
			return EasyObj.valueOf(columnClass,value);
	}

	private Class getColumnClass(String columnName){
		ColumnMetaData obj=RowUtils.findColumnMetaData(this.getClass(),columnName);
		if(obj==null)
			return null;
		return obj.clazz;
	}

	private boolean isSameValue(String key,Object newvalue){
		Object oldvalue=record==null?null:get(key);
		boolean result=false;
		if(oldvalue==newvalue)
			result=true;
		else if(newvalue==null||oldvalue==null)
			result=false;
		else if(newvalue instanceof Number && oldvalue instanceof Number)
			result=esdk.math.eq((Number)newvalue,(Number)oldvalue);
		else if(newvalue instanceof Date && oldvalue instanceof Date)
			result=((Date)newvalue).getTime()==((Date)oldvalue).getTime();
		else if(newvalue instanceof Date && oldvalue instanceof LocalDateTime)
			result=esdk.time.toLocalDateTime((Date)newvalue).equals(oldvalue);
		else if(newvalue instanceof LocalDateTime && oldvalue instanceof Date)
			result=esdk.time.toDate((LocalDateTime)newvalue).equals(oldvalue);
		else
			result=esdk.obj.eq(newvalue,oldvalue);
		return result;
	}

	private void updateBuffer(){
		if((isExistRecord||isForceUpdate)&&isChanged){
			sqlOperator=new Update(getTableName(),conn);
			Update sqler=(Update)sqlOperator;
			sqler.eq(getPrimaryKeyName(),getPrimaryKey());
			String[] tableColumnNames=getColumns();
			for(String key:changedMap.keySet()){
				if(!esdk.array.contains(tableColumnNames, key)) continue;
				if(!(isForceUpdate&&isAutoIncrement&&key.equals(getPrimaryKeyName()))){
					Object value=changedMap.get(key);
					if(value instanceof LiteralColumn||value instanceof Literal)
						sqler.setFieldExpr(key,value.toString());
					else if(value instanceof com.esdk.sql.Field field){
						sqler.setField(key,field);
					}
					else
						sqler.setField(key,value);
				}
			}
		}
	}

	private void insertBuffer(){
		if(isChanged){
			sqlOperator=new Insert(getTableName(),conn);
			Insert insert=(Insert)sqlOperator;
			insert.setAutoIncrement(isAutoIncrement);
			if(SQLAssistant.isPostgreSQL())
				insert.setPrimaryKeyName(getPrimaryKeyName());
			if(isAutoIncrement&&SQLAssistant.isOracle()){ //eg. SEND_LOG_SEQ_ID
				insert.setField(getPrimaryKeyName(),new Function("SEQ_"+getTableName()+".NEXTVAL"));
				insert.setPrimaryKeyName(getPrimaryKeyName());
			}
			if(!isAutoIncrement&&this.getPrimaryKey()==null){
				this.setPrimaryKey(RowUtils.genNextPrimaryId());
				insert.setField(this.getPrimaryKeyName(),this.getPrimaryKey());
			}
			for(int i=0,n=this.getColumns().length;i<n;i++){
				String key=this.getColumns()[i];
				if(record.get(key)!=null)
					insert.setField(key,record.get(key));
			}
		}
	}

	public T fillCommonFields() {
		if(!isExistRecord() && this.hasColumn(Constant.CreateTime)&&get(Constant.CreateTime)==null){
			this.set(Constant.CreateTime,new Date());
			if(this.hasColumn(Constant.Valid) && this.get(Constant.Valid)==null)
				this.set(Constant.Valid,true);
		}
		if(isChanged && this.hasColumn(Constant.UpdateTime) && (get(Constant.UpdateTime)==null&&!getChanged().containsKey(Constant.UpdateTime))){
			this.set(Constant.UpdateTime,new Date());
			if(this.hasColumn(Constant.Version)&&this.isExistRecord){ //TODO version字段比较特殊，系统会自动加+1，不适合人为处理
//				this.set(Constant.Version,new Literal(Constant.Version+"+1"));
				this.set(Constant.Version,(short)(this.get(Constant.Version,0)+1));
			}
		}
		if(this.hasColumn(Constant.Valid) && esdk.obj.isFalse(getBoolean(Constant.Valid)) && isChanged && this.hasColumn(Constant.DeleteTime) && (get(Constant.DeleteTime)==null||!getChanged().containsKey(Constant.DeleteTime)))
			this.set(Constant.DeleteTime,new Date());
		return (T)this;
	}

	private void saveBuffer(){
		if(isExistRecord||isForceUpdate)
			updateBuffer();
		else
			insertBuffer();
	}

	public void insert(){
		operatorFlag=INSERT;
	}

	public void save(boolean ifForceUpdate){
		if(ifForceUpdate){
			if(getPrimaryKey()==null)
				insert();
			else if(!isExistRecord())
				update(ifForceUpdate);
		}else
			save();
	}

	public void save(){
		operatorFlag=SAVE;
	}

	public void update(){
		operatorFlag=UPDATE;
	}

	public void update(boolean isForce){
		operatorFlag=UPDATE;
		isForceUpdate=isForce;
	}

	public void delete(){
		operatorFlag=DELETE;
	}

	@JSONField(serialize=false)
	private AbstractSelect getSelect(){
		String selectClassName=this.getClass().getName().replaceFirst("Row$","Select");
		AbstractSelect sqler=esdk.reflect.safeNewInstance(selectClassName);
		sqler.setConnection(conn);
		// Select sqler=new Select(getTableName(),conn);
		sqler.setTop(1);
		sqler.eq(getPrimaryKeyName(),getPrimaryKey());
		sqler.useSessionCache(useSessionCache).useCache(useCacheSec);
		return sqler;
	}

	public T refresh() throws SQLException{
		AbstractSelect select=getSelect();
		refreshRecord(select);
		return (T)this;
	}

	private void refreshRecord(AbstractSelect select) throws SQLException{
		T firstRow=(T)select.getFirstRow(true);
		this.clear();
		this.load(firstRow);
		this.isExistRecord=firstRow.isExistRecord;
	}

	protected void loadFromResultSet(ResultSet rset) throws SQLException{
		String fieldName=null;
		try{
			ResultSetMetaData rsmd=rset.getMetaData();
			for(int i=1,n=rsmd.getColumnCount()+i;i<n;i++){
				fieldName=rsmd.getColumnLabel(i);
				record.put(fieldName,RowUtils.getCurrentValue(rset,rsmd,i));
			}
		}catch(SQLException e){
			throw new SQLException("Field["+fieldName+"] Error: "+e.toString());
		}
	}

	public void clearDirty(){
		initMaps();
		changedMap.clear();
		originalMap.clear();
		newVaulesMap.clear();
		isChanged=false;
	}

	private void clear(){
		Object pkid=getPrimaryKey();
		record.clear();
		record.put(getPrimaryKeyName(),pkid);
		initMaps();
		changedMap.clear();
		originalMap.clear();
		newVaulesMap.clear();
		isChanged=false;
	}

	public T refresh(Object pk,Connection conn) throws SQLException{
		setConnection(conn);
		refresh(pk);
		return (T)this;
	}

	public T refresh(Object pk) throws SQLException{
		if(pk==null)
			return (T)this;
		record.put(getPrimaryKeyName(),pk);
		return refresh();
	}

	private void deleteBuffer(){
		if(getPrimaryKey()!=null){
			sqlOperator=new Delete(getTableName(),conn);
			Delete sqler=(Delete)sqlOperator;
			sqler.eq(getPrimaryKeyName(),getPrimaryKey());
			isChanged=true;// isChange or isAllowCommit?
		}
	}

	public boolean isChanged(){
		return isChanged;
	}

	@JSONField(serialize=false)
	public Map<String,Object> getChanged(){
		return changedMap;
	}

	@JSONField(serialize=false)
	public Map<String,Object> getNewValues(){
		return this.newVaulesMap;
	}

	public Map getOriginalMap(){
		initMaps();
		return originalMap;
	}

	public Object findOriginalValue(String fieldName){
		initMaps();
		return originalMap.get(fieldName);
	}

	public boolean checkUnique(String...uniqueFields) throws Exception{
		if(uniqueFields.length>0){
			Select s=new Select(this.getTableName(),conn);
			s.setTop(0);
			for(int i=0;i<uniqueFields.length;i++){
				s.addEqualCondition(uniqueFields[i],this.get(uniqueFields[i]));
			}
			if(getPrimaryKey()!=null)
				s.notEq(getPrimaryKeyName(),getPrimaryKey());
			return s.count()==0;
		}
		return true;
	}

	public boolean checkUniqueRecord() throws Exception{
		Field field=EasyReflect.findField(getMetaData().getClass(),"UniqueIndexFields",false);
		String[] uniqueIndexFields=(String[])field.get(getMetaData());
		return checkUnique(uniqueIndexFields);
	}

	public T saveUniqueRecord() throws Exception{
		Field field=EasyReflect.findField(getMetaData().getClass(),"UniqueIndexFields",false);
		String[] uniqueIndexFields=(String[])field.get(getMetaData());
		if(uniqueIndexFields.length>0){
			Select s=new Select(this.getTableName(),conn);
			s.setTop(0);
			for(int i=0;i<uniqueIndexFields.length;i++){
				s.addEqualCondition(uniqueIndexFields[i],this.get(uniqueIndexFields[i]));
			}
			if(getPrimaryKey()!=null)
				s.addNotEqualCondition(getPrimaryKeyName(),getPrimaryKey());
			IRow dbRow=s.getFirstRow();
			if(dbRow!=null){
				ParentRow originalParentRow=((ParentRow)this.clone());
				originalParentRow.clear();
				originalParentRow.record=(HashMap)this.record;
				this.record=(HashMap)dbRow.record();
				this.clearDirty();
				this.load(originalParentRow);
				this.isExistRecord=true;
			}
		}
		return (T)this;
	}

	public Response validate(){
		return RowUtils.validate(this);
	}

	public boolean checkDirty() throws SQLException{
		boolean result=false;
		initMaps();
		if(isCheckDirty&&isChanged()&&(sqlOperator instanceof Update)){
			Select select=new Select(getTableName(),this.conn);
			select.addEqualCondition(getPrimaryKeyName(),getPrimaryKey());
			String[] changedColumns=getChangedColumns();
			select.setColumns(changedColumns);
			for(int i=0;i<changedColumns.length;i++){
				select.addEqualCondition(changedColumns[i],originalMap.get(changedColumns[i]));
			}
			result=!select.isExistRecord();
			select.clear();
			if(result){
				throw new SQLException("table:"+getTableName()+" pkid:"+getPrimaryKey()+" can not perform update because columns["+EasyStr.arrToStr(getChangedColumns(),'|')+"] is dirty");
			}
		}
		return result;
	}

	/**如果更新会用乐观锁检查version字段*/
	public boolean performWithCheckVersion() throws SQLException{
		boolean result=true;
		if(sqlOperator instanceof Update update && isCheckVersion && hasColumnName(VersionFieldName)){
			if(getChanged().containsKey(VersionFieldName)) {
				result=false; //这段是多余的，只是为了方便理解
			}else{
				Number oldVersion=(Number)esdk.obj.or(originalMap.get(VersionFieldName),get(VersionFieldName));
				Number newVersion=(Number)esdk.obj.or(getChanged().get(VersionFieldName),esdk.math.toInt(get(VersionFieldName))+1);
				update.eq(VersionFieldName,oldVersion);
				update.setField(VersionFieldName,newVersion);
				update.perform();
				result=update.getUpdatedCount()==1;
			}
			if(!result){
				throw new SQLException("table:"+getTableName()+" pkid:"+getPrimaryKey()+" can not perform update because the version is conflicted.");
			}
		}else{
			result=sqlOperator.perform()==1;
		}
		return result;
	}

	/**不通过版本号检查，而是通过修改的字段检查，这方法不常用*/
	public boolean performWithCheckDirty() throws SQLException{
		initMaps();
		boolean result=false;
		if(isCheckDirty&&isChanged()&&(sqlOperator instanceof Update update)){
			String[] changedColumns=getChangedColumns();
			for(int i=0;i<changedColumns.length;i++){
				update.addEqualCondition(changedColumns[i],originalMap.get(changedColumns[i]));
			}
			update.perform();
			result=update.getUpdatedCount()==1;
			if(!result)
				throw new SQLDirtyException(
						"table:"+getTableName()+" pkid:"+getPrimaryKey()+" can not perform update because columns["+EasyStr.arrToStr(getChangedColumns(),'|')+"] is dirty");
		}else{
			result=sqlOperator.perform()==1;
		}
		return result;
	}

	@JSONField(serialize=false)
	private String[] getChangedColumns(){
		CharAppender result=new CharAppender(',');
		for(Iterator iter=getChanged().keySet().iterator();iter.hasNext();){
			String column=(String)iter.next();
			if(hasColumnName(column)){
				result.append(column);
			}
		}
		return EasyStr.split(result.toString());
	}

	@JSONField(serialize=false)
	public void checkDirty(boolean value){
		isCheckDirty=value;
		if(isCheckDirty)
			isCheckVersion=false;
	}

	/**是否使用乐观锁，在执行update时检查version*/
	@JSONField(serialize=false)
	public void checkVersion(boolean value){
		isCheckVersion=value;
		if(isCheckVersion)
			isCheckDirty=false;
	}

	public boolean flush() throws SQLException{
		boolean result=false;
		result=preCommit();// defaul value is true,return true when not changed and not any
		if(result){
			if(isChanged&&sqlOperator!=null){
				sqlOperator.showSql(this.isPrintSql);
				if(sqlOperator instanceof Update update){
					if(isCheckDirty)
						/* checkDirty(); */
						result=performWithCheckDirty();
					else if(isCheckVersion)
						result=performWithCheckVersion();
					else
						result=update.perform()==1;
				}else
					result=sqlOperator.perform()==1;
			}
			if(result){
				switch(operatorFlag){
				case INSERT:
				case SAVE:
					isExistRecord=true;
					if(isAutoIncrement&&sqlOperator instanceof Insert insert){
						Object generatedKey=insert.getGeneratedKey();
						setPrimaryKey(generatedKey);
					}
					break;
				case DELETE:
					isExistRecord=false;
				default:
					break;
				}
				isChanged=false;
				changedMap.clear();
				originalMap.clear();
				newVaulesMap.clear();
			}
		}
		return result;
	}

	public String showSQL(){
		if(sqlOperator==null)
			pretreat();
		return sqlOperator.getSQL();
	}

	public T showSQL(boolean isPrintSql){
		this.isPrintSql=isPrintSql;
		return (T)this;
	}

	public boolean preCommit(){
		if(conn==null)
			throw new SQLRuntimeException("please invoke setConnection() before commit()");
		pretreat();
		return true;
	}

	private void pretreat(){
		initMaps();
		switch(operatorFlag){
		case INSERT:
			insertBuffer();
			break;
		case UPDATE:
			updateBuffer();
			break;
		case DELETE:
			deleteBuffer();
			break;
		case SAVE:
			saveBuffer();
			break;
		default:
			saveBuffer();
		}
	}

	public void testinsert(){
		changedMap=record;
		isChanged=true;
	}

	@JSONField(serialize=false)
	ISQL getISQL(){// specialize to Transaction
		pretreat();
		return sqlOperator;
	}

	@JSONField(serialize=false)
	public boolean isSaved(){
		return !isChanged;
	}

	public T clone(){
		ParentRow result=newSelf();
		result.isChanged=this.isChanged;
		result.isExistRecord=this.isExistRecord;
		result.operatorFlag=this.operatorFlag;
		result.isAutoIncrement=this.isAutoIncrement;
		result.isCheckDirty=this.isCheckDirty;
		result.isCheckVersion=this.isCheckVersion;
		result.columns=this.columns;
		result.isForceUpdate=this.isForceUpdate;
		result.allowSetNullValue=this.allowSetNullValue;
		result.record=(HashMap)this.record.clone();
		if(changedMap!=null)
			result.changedMap=(HashMap)this.changedMap.clone();
		if(changedMap!=null)
			result.newVaulesMap=(HashMap)this.newVaulesMap.clone();
		if(originalMap!=null)
			result.originalMap=(HashMap)this.originalMap.clone();
		result.conn=this.conn;
		return (T)result;
	}

	public boolean isExistRecord(){
		return this.isExistRecord;
	}

	/**手工修改是否存在记录标记，避免再去查数据库，保持isExistRecord的业务一致性*/
	public T markDeleted(){
		this.isExistRecord=false;
		return (T)this;
	}

	public Object[] toArray() {
		return RowUtils.toArray(this);
	}

	public Object[] toArray(String...labels) {
		return RowUtils.toArray(this,labels);
	}

	public List toList(){
		return RowUtils.toList(this);
	}

	public List toList(String...labels){
		return RowUtils.toList(this,labels);
	}

	@Override
	public String toXml(){
		return XML.toXml(JSONUtil.parseObj(toMap(true)),Constant.RowXmlIdentifier);
	}

	@Override
	public String toXml(String...labels){
		return XML.toXml(JSONUtil.parseObj(toMap(true,labels)),Constant.RowXmlIdentifier);
	}
	public String toCsv(){
		return toCsv(true);
	}
	public String toCsv(boolean isExportHeaders){
		TString result=new TString();
		if(isExportHeaders){
			result.append(new CharAppender(',').add(getColumns()));
		}
		CharAppender ca=new CharAppender(',');
		for(int j=0;j<getColumns().length;j++){
			ca.append(EasyCsv.csvEncode(esdk.str.getText(getString(getColumns()[j]))));
		}
		if(isExportHeaders&&ca.length()>0)
			result.appendNewLine();
		result.append(ca);
		return result.toString();
	}
	@Override
	public String toCsv(String...labels){
		return toCsv(true,labels);
	}
	@Override
	public String toCsv(boolean isExportHeaders,String...labels){
		TString result=new TString();
		if(isExportHeaders)
			result.append(new CharAppender(',').add(labels));
		CharAppender ca=new CharAppender(',');
		for(int j=0;j<labels.length;j++){
			ca.append(EasyCsv.csvEncode(esdk.str.getText(getString(labels[j]))));
		}
		if(isExportHeaders&&ca.length()>0)
			result.appendLine(ca);
		return result.toString();
	}

	@JSONField(serialize=false)
	public String[] getColumns(){
		if(columns==null){
			String classname=this.getClass().getName();
			classname=classname.substring(0,classname.length()-3).concat("MetaData");
			try{
				columns=(String[])Class.forName(classname).getField("FieldNames").get(this);
			}catch(Exception e){
				throw new RuntimeException(e);
			}
		}
		return columns;
	}

	public void load(Object[] csv) throws Exception{
		int i=0;
		for(Iterator iter=record.keySet().iterator();iter.hasNext();){
			String key=(String)iter.next();
			put(key,csv[i++]);
		}
	}

	/**加载时可过滤通用字段*/
	public T load(ParentRow row,boolean excludeCommonFields){
		return load(row,null,excludeCommonFields?Constant.SystemFields:null);
	}

	/**加载时可指定白名单和黑名单*/
	public T load(IRow row,String[] includesFields,String[] excludeFields){
		if(row==null||row==this)
			return (T)this;
		String key=null;
		for(Iterator iter=Arrays.asList(getColumns()).iterator();iter.hasNext();){
			key=(String)iter.next();
			if(row.hasColumnName(key)
					&& (esdk.array.isBlank(includesFields)||esdk.array.contains(includesFields,key))
					&& (esdk.array.isBlank(excludeFields)||!esdk.array.contains(excludeFields,key)))
				put(key,row.get(key));
		}
		return (T)this;
	}

	public T load(T parentRow){
		this.record=(HashMap<String,Object>)parentRow.record.clone();
		this.isExistRecord=parentRow.isExistRecord;
		return (T)this;
	}

	public T load(IRow row){
		if(row==null||row==this)
			return (T)this;
		String key=null;
		try{
			for(Iterator iter=Arrays.asList(getColumns()).iterator();iter.hasNext();){
				key=(String)iter.next();
				if(row.hasColumnName(key))
					put(key,row.get(key));
			}
			return (T)this;
		}catch(Exception e){
			esdk.serr("字段【"+key+"】载入错误...");
			throw new RuntimeException(e);
		}
	}

	public T load(IRow row,Map<String,String> fieldsMapping){
		return load(row,false,fieldsMapping);
	}

	public T load(IRow row,boolean acceptNull){
		return load(row,acceptNull,(Map<String,String>)Constant.EmptyMap);
	}

	public T load(IRow srcRow,boolean acceptNull,Map<String,String> fieldsMap){
		if(srcRow==null)
			return (T)this;
		try{
			for(String key:getColumns()){
				Object newValue=srcRow.get(esdk.obj.or(fieldsMap.get(key),key));
				if(acceptNull||newValue!=null)
					put(key,newValue);
			}
			return (T)this;
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}

	/**
	 * ignoreLoadFields
	 * @param row 为空的值将不会加载到当前对象
	 * @param ignoreLoadFields 针对当前对象而言
	 * @return
	 */
	@SafeVarargs
	public final T load(IRow row, FieldGetter<T> ...ignoreLoadFields){
		if(row==null||row==this)
			return (T)this;
		String key=null;
		Object val = null;
		String[] ignoreArr = Stream.of(ignoreLoadFields).map(FieldGetter::getTargetField).map(esdk.str::toUnderlineCase).toArray(String[]::new);
		try{
			for(Iterator iter=Arrays.asList(getColumns()).iterator();iter.hasNext();){
				key=(String)iter.next();
				val = row.get(key);
				if(val != null && !esdk.array.contains(ignoreArr, key) && row.hasColumnName(key))
					put(key,val);
			}
			return (T)this;
		}catch(Exception e){
			esdk.serr("字段【"+key+"】载入错误...");
			throw new RuntimeException(e);
		}
	}

	/**
	 * @param acceptNull:
	 * true：允许加载null值
	 * false：忽略null值
	 * */
	public T load(IRow row,boolean acceptNull,String[] includes){
		if(row==null)
			return (T)this;
		try{
			for(int i=0;i<includes.length;i++){
				String key=includes[i];
				Object newValue=row.get(key);
				if(acceptNull||newValue!=null)
					put(key,newValue);
			}
			return (T)this;
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}

	@Override
	public boolean hasColumnName(String name){
		return esdk.array.contains(getColumns(),name);
	}

	public boolean hasColumn(String column){
		return EasyReflect.hasPropertyName(this.getMetaData().getClass(),esdk.str.toCamelCase(column,true));
	}

	@Override
	public Map toMap(){
		return (Map)record.clone();
	}

	public void setAutoIncrement(boolean isAutoIncrement){
		this.isAutoIncrement=isAutoIncrement;
	}

	/**默认输出驼峰格式，且输出扩展字段*/
	@Override
	public JSONObject toJsonObject(){
		return toJsonObject(true,true);
	}

	public <B> B toBean(Class<B> cls){
		return (B)esdk.reflect.copyBeanProperties(this,esdk.reflect.safeNewInstance(cls));
  }

	@Override
	public JSONObject toJsonObject(String...columns){
		HashMap result=new HashMap();
		for(int i=0;i<columns.length;i++){
			result.put(columns[i],this.record.get(columns[i]));
		}
		return RowUtils.toJsonObject(result,false,true);
	}

	public JSONObject toJsonObject(boolean isFormatJavaBeanName){
		return toJsonObject(isFormatJavaBeanName,this.getColumns());
	}
	public JSONObject toJsonObject(boolean isFormatJavaBeanName,boolean includeExtendFields){
		return toJsonObject(isFormatJavaBeanName,includeExtendFields,true);
	}
	public JSONObject toJsonObject(boolean isFormatJavaBeanName,boolean includeExtendFields,boolean ifSnowIdToStr){
		if(isFormatJavaBeanName){
			String[] columns=includeExtendFields&&!this.record.isEmpty()
				?esdk.array.distinct(getColumns(),esdk.array.toArray(this.record.keySet()))
				:this.getColumns();
			return toJsonObject(true,columns);
		}
		return RowUtils.toJsonObject(this.record,isFormatJavaBeanName,ifSnowIdToStr);
	}

	@Override
	public JSONObject toJsonObject(boolean isFormatJavaBeanName,String...columns){
		if(columns.length==0)
			columns=getColumns();
		return RowUtils.toJsonObject(this.record,isFormatJavaBeanName,true);
	}

	public Map toMap(String...columns){
		return toMap(false,columns);
	}

	@Override
	public Map toMap(boolean isFormatJavaBeanName,String...columns){
		return toMap(isFormatJavaBeanName,columns,esdk.array.EmptyStrArr);
	}

	@Override
	public Map toMap(boolean isFormatJavaBeanName,String[] columns,String[] excludeCols){
		HashMap result=new HashMap();
		if(columns.length==0)
			columns=esdk.str.toArray(record.keySet());
		for(int i=0;i<columns.length;i++){
			if(!esdk.str.existOf(excludeCols,columns[i]))
				result.put(isFormatJavaBeanName?EasyStr.toCamelCase(columns[i]):columns[i],this.record.get(columns[i]));
		}
		return result;
	}

	public T load(AbstractSelect aselect){
		this.load(aselect.fieldMap);
		return (T)this;
	}

	/**默认忽略null值*/
	@Override
	public T load(Map rowMap){
		return load(rowMap,false,false,true);
	}

	/**ignoreNull:如果要输入null,则为false，如果忽略null，则为true*/
	public T load(Map map,boolean ignoreNull){
		Collection<String> cols=esdk.array.overlap(map.keySet(),Arrays.asList(getColumns()));
		for(String colName:cols){
			Object val=map.get(colName);
			if(!ignoreNull || val!=null)
				this.put(colName,val);
		}
		return (T)this;
	}

	/**
	 * @param ignoreNull 如果要输入null,则为false，如果忽略null，则为true，默认应忽略null
	 *isFormatJavaBeanProperty：为true可以自适应下划线或大小写的columnName写值到Row。
	 *checkExistColumn：为true则限制必须存在这个columnName才允许写值到Row
	 */
	@Override
	public T load(Map map,boolean ignoreNull,boolean checkExistColumn,boolean isFormatJavaBeanProperty){
		for(Entry<String,?> entry:((Map<String,?>)map).entrySet()){
			String columnName=entry.getKey();
			boolean foundColumn=false;
			if(this.hasColumnName(columnName))
				foundColumn=true;
			else if(isFormatJavaBeanProperty){
				columnName=esdk.str.toUnderlineCase((String)entry.getKey());
				if(this.hasColumnName(columnName))
					foundColumn=true;
				else{
					columnName=(String)entry.getKey();
				}
			}
			if(!checkExistColumn||foundColumn){
				if(!ignoreNull || entry.getValue()!=null)
					this.put(columnName,entry.getValue());
			}
		}
		return (T)this;
	}

	public T load(Object bean){
		return (T)load(bean,false);
	}

	/**
	 * 大多数情况下直接调用该方法即可
	 * 忽略空值，属性名全等匹配，类型不匹配强转
	 * 可声明忽略的字段
	 */
	public T load(Object bean,String ...excludeFields){
		return load(bean,null,false,null,esdk.regex.toStrictMatch(false,esdk.array.merge(RowUtils.Keywords, excludeFields)));
	}
	@SafeVarargs
	public final T load(Object bean,FieldGetter<T> ...fieldGetters){
		String[] excludeFields = new String[fieldGetters.length];
		for (int i = 0; i < fieldGetters.length; i++) {
			excludeFields[i] = fieldGetters[i].getTargetField();
		}
		return load(bean,null,false,null,esdk.regex.toStrictMatch(false,esdk.array.merge(RowUtils.Keywords, excludeFields)));
	}

	public T load(Object bean,boolean acceptNull){
		return load(bean,null,acceptNull,null);
	}
	public T load(Object bean,boolean acceptNull,String includesRegex){
		return load(bean,null,acceptNull,includesRegex);
	}

	public T load(Object bean,Map map){
		return (T)load(bean,map,false,null);
	}

	public T load(Object bean,Map map,String includesRegex){
		return (T)load(bean,map,false,includesRegex);
	}

	public T load(Object bean,Map map,boolean acceptNull,String includesRegex){
		return load(bean,map,acceptNull,includesRegex,esdk.regex.toStrictMatch(false,RowUtils.Keywords));
	}

	public T load(Object bean,Map map,boolean acceptNull,String includesRegex,String excludesRegex){
		EasyReflect.copyBeanProperties(bean,this,acceptNull,false,map,includesRegex,excludesRegex);
		return (T)this;
	}

	@Override
	public String toString(){
		return esdk.json.toJSONString(toJsonObject(),esdk.json.getSerializeConfig());
	}

	@Override
	public int hashCode(){
		return toString().hashCode();
	}

	@Override
	public boolean equals(Object o){
		return RowUtils.equals(this,(IRow)o);
	}

	@Override
	public Boolean getBoolean(String key){
		return EasyObj.convert(get(key),Boolean.class);
	}

	@Override
	public Double getDouble(String key){
		return EasyObj.convert(get(key),Double.class);
	}

	@Override
	public Float getFloat(String key){
		return EasyObj.convert(get(key),Float.class);
	}

	@Override
	public Integer getInteger(String key){
		return EasyObj.convert(get(key),Integer.class);
	}

	@Override
	public Long getLong(String key){
		return EasyObj.convert(get(key),Long.class);
	}

	@Override
	public Short getShort(String key){
		return EasyObj.convert(get(key),Short.class);
	}

	@Override
	public String getString(String key){
		return EasyObj.convert(get(key),String.class);
	}

	public Date getDate(String key){
		return (Date)EasyObj.convert(get(key),Date.class);
	}
	public LocalDateTime getLocalDateTime(String key){
		return (LocalDateTime)EasyObj.convert(get(key),LocalDateTime.class);
	}
	@Override
	public BigDecimal getBigDecimal(String key){
		return EasyObj.convert(get(key),BigDecimal.class);
	}

	@JSONField(serialize=false)
	public boolean isAutoIncrement(){
		return this.isAutoIncrement;
	}

	public T useCache(int cacheSec){
		this.useCacheSec=cacheSec;
		return (T)this;
	}

	public T useSessionCache(boolean useSessionCache){
		this.useSessionCache=useSessionCache;
		return (T)this;
	}

	public void allowSetNullValue(boolean value){
		this.allowSetNullValue=value;
	}

	public T setNull(String ...filedNameArr){
		boolean copy = allowSetNullValue;
		allowSetNullValue = true;
		for(String filedName : filedNameArr)
			setNewValue(filedName, null);
		allowSetNullValue = copy;
		return (T) this;
	}

	@Override
	public String getPrimaryKeyName(){
		if(primaryKeyName==null)
			primaryKeyName=(String)esdk.reflect.getFieldValue(getMetaData(),"PrimaryKey");
		return primaryKeyName;
	}

	@Override
	public String getTableName(){
		if(tableName==null)
			tableName=(String)esdk.reflect.getFieldValue(getMetaData(),"TABLENAME");
		return tableName;
	}

	public T setTableName(String tableName){
		this.tableName=tableName;
		return (T)this;
	}

	@Override
	public int compareTo(T obj){
		return new ComparableArray(this.toArray()).compareTo(new ComparableArray(((IRow)obj).toArray()));
	}

	public T incr(String fieldName,int i){
		setNewValue(fieldName,new com.esdk.sql.Field(fieldName).incr((long)i));
		return (T)this;
	}

	public T decr(String fieldName,int i){
		setNewValue(fieldName,new com.esdk.sql.Field(fieldName).decr((long)i));
		return (T)this;
	}
}
